<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width">
    <meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1">
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
    <meta name="description" content="Library of precisely prepared monochromatic icons and symbols, created with an emphasis on simplicity and easy orientation.">
    <link rel="shortcut icon" href="https://lh3.googleusercontent.com/-3l76eKixr94/AAAAAAAAAAI/AAAAAAAAAAA/ACnBePZFmApY3Em5W0q-ckNSU6biShGiKw/s96-c-mo/photo.jpg">
    <title>雪兔测试</title>
    <style>
    body {
        overflow: hidden;
        background-color: #000;
    }

    canvas {
        width: 100%;
        height: 100vh;
    }
    </style>
</head>
<body>
    <canvas width="980" height="1306"></canvas>
    <script>
    window.onload = () => {
        const CANVAS = document.getElementsByTagName("canvas")[0];
        const CTX = CANVAS.getContext("2d");
        const CHARS = [];
        const MAX_CHARS = 200;
        const SEPARATION = 1.5;

        let ww, wh, camera;

        class Vector {
            constructor(x, y, z) {
                this.x = x;
                this.y = y;
                this.z = z;
            }

            rotate(dir, ang) {
                const X = this.x;
                const Y = this.y;
                const Z = this.z;

                const SIN = Math.sin(ang);
                const COS = Math.cos(ang);

                if (dir === "x") {
                    this.y = Y * COS - Z * SIN;
                    this.z = Y * SIN + Z * COS;
                } else if (dir === "y") {
                    this.x = X * COS - Z * SIN;
                    this.z = X * SIN + Z * COS;
                }
            }

            project() {
                const ZP = this.z + camera.z;
                const DIV = ZP / 600;
                const XP = (this.x + camera.x) / DIV;
                const YP = (this.y + camera.y) / DIV;
                const CENTER = getCenter();
                return [XP + CENTER[0], YP + CENTER[1], ZP];
            }
        }

        class Char {
            constructor(letter, pos) {
                this.letter = letter;
                this.pos = pos;
            }

            rotate(dir, ang) {
                this.pos.rotate(dir, ang);
            }

            render() {
                const PIXEL = this.pos.project();
                const XP = PIXEL[0];
                const YP = PIXEL[1];
                const MAX_SIZE = 50;
                const SIZE = (1 / PIXEL[2] * MAX_SIZE) | 0;
                const BRIGHTNESS = SIZE / MAX_SIZE;
                const COL = `rgba(255, 255, ${100 * BRIGHTNESS | 0 + 150}, ${BRIGHTNESS})`;

                CTX.beginPath();
                CTX.fillStyle = COL;
                CTX.font = SIZE + "px monospace";
                CTX.fillText(this.letter, XP, YP);
                CTX.fill();
                CTX.closePath();
            }
        }

        function getCenter() {
            return [ww / 2, wh / 2];
        }

        function signedRandom() {
            return Math.random() - Math.random();
        }

        function render() {
            for (let i = 0; i < CHARS.length; i++) {
                CHARS[i].render();
            }
        }

        let time = 0;

        function update() {
            CTX.clearRect(0, 0, ww, wh);
            for (let i = 0; i < CHARS.length; i++) {
                const DX = 0.005 * Math.sin(time * 0.001);
                const DY = 0.005 * Math.cos(time * 0.001);
                CHARS[i].rotate("x", DX);
                CHARS[i].rotate("y", DY);
            }
            ++time;
        }

        function loop() {
            window.requestAnimationFrame(loop);
            update();
            render();
        }

        function getRandomInt(min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        };

        function createChars() {
            for (let i = 0; i < MAX_CHARS; i++) {
                const CHARACTER = String.fromCharCode((Math.random() * 93 + 34) | 0);
                const X = signedRandom() * SEPARATION;
                const Y = signedRandom() * SEPARATION;
                const Z = signedRandom() * SEPARATION;
                const POS = new Vector(X, Y, Z);
                const CHAR = new Char(CHARACTER, POS);
                CHARS.push(CHAR);
            }
        }

        function setDim() {
            ww = window.innerWidth;
            wh = window.innerHeight;
            CANVAS.width = ww;
            CANVAS.height = wh;
        }

        function initCamera() {
            camera = new Vector(0, 0, SEPARATION + 1);
        }

        window.onresize = setDim;

        (() => {
            setDim();
            initCamera();
            createChars();
            loop();
        })();
    };
    </script>
</body>
</html>